#if !defined(RECYCLABLE_BUFFERS_H)
#define RECYCLABLE_BUFFERS_H

#include <vector>
#include <iostream>
	//std::cerr

#include "tbb/concurrent_queue.h"
    //tbb::concurrent_queue
#include "tbb/spin_mutex.h"
    //tbb::spin_mutex
#include <boost/shared_ptr.hpp>
    //boost::shared_ptr;

//########################################################################################

//	 t_recyclable_buffers
//  
//      thread safe list of buffer with free list recycling memory.
//      Remember to reinitialise after allocate
// 
//########################################################################################

template<typename DATA_TYPE, typename INIT_DATA_TYPE>
class t_recyclable_buffers
{
    // typedefs
    typedef std::vector<boost::shared_ptr<DATA_TYPE> >      t_vec_sp_data;
    typedef tbb::concurrent_queue<DATA_TYPE*>               t_queue_free;

    // data
    static t_vec_sp_data            data_buffers;
    static t_queue_free             free_list;
    static INIT_DATA_TYPE           init;

	// // spin_mutex no longer necessary because of concurrent_queue
	// static tbb::spin_mutex allocation_mutex;
public:
    t_recyclable_buffers(INIT_DATA_TYPE init_,  std::size_t buffers_in_flight_hint = 10U)
    {
        init = init_;

        //
        //  list of storage buffers
        // 
        data_buffers.reserve(buffers_in_flight_hint);
        for (unsigned ii = 0; ii < buffers_in_flight_hint; ++ii)
        {
            data_buffers.push_back(boost::shared_ptr<DATA_TYPE>(new DATA_TYPE(init)));
            //	Free list 
            free_list.push(data_buffers.back().get());
        }
    }

    static DATA_TYPE* allocate()
    {
        DATA_TYPE* next_free;
        while(!free_list.try_pop(next_free) ) 
        {
            // 
            // Only one thread allocates extra items at a time
            //
            data_buffers.push_back(boost::shared_ptr<DATA_TYPE>(new DATA_TYPE(init)));
            //	Free list 
            free_list.push(data_buffers.back().get());
        }
	
        return next_free;
    }
	
    static void deallocate(DATA_TYPE* next_free)
    {
        free_list.push(next_free);
    }

};

//
//  static data
//
template <typename T, typename U>
U t_recyclable_buffers<T, U>::init;
template <typename T, typename U>
typename t_recyclable_buffers<T, U>::t_vec_sp_data   t_recyclable_buffers<T, U>::data_buffers;
template <typename T, typename U>
typename tbb::concurrent_queue<T*> t_recyclable_buffers<T, U>::free_list;
//template <typename T, typename U>
//typename tbb::spin_mutex t_recyclable_buffers<T, U>::allocation_mutex;
#endif	// RECYCLABLE_BUFFERS_H


